package com.lcc.lynx.security.wrapper;

import cn.hutool.core.util.URLUtil;
import com.lcc.lynx.common.utils.IOUtils;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.Getter;

import java.io.*;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;

/**
 * 构建可重复读取inputStream的request
 *
 * @author Fanyc
 */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {

    /**
     * 原始请求
     */
    private final HttpServletRequest originalRequest;

    @Getter
    private final Date startTime;

    private String body;

    public String getBody() throws IOException {
        if(body == null){
            if(originalRequest.getMethod().equalsIgnoreCase("get")){
                body = URLUtil.decode(originalRequest.getQueryString());
            }else{
                getInputStream();
            }
        }
        return body;
    }

    /**
     * 请求内容Buffer
     */
    private ByteArrayInputStream bais=null;

    public RepeatedlyRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.originalRequest = request;
        this.startTime = new Date();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 如果没有读过则从原始流中读取
        if (this.bais==null) {
            byte[] buffer = IOUtils.readBytes(this.originalRequest.getInputStream());
            body = new String(buffer);
            this.bais = new ByteArrayInputStream(buffer);
        }
        this.bais.reset();

        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public int read(byte[] b, int off, int len) throws IOException {
                return bais.read(b, off, len);
            }

            @Override
            public long skip(long n) throws IOException {
                return bais.skip(n);
            }

            @Override
            public int available() throws IOException {
                return bais.available();
            }

            @Override
            public boolean markSupported() {
                return bais.markSupported();
            }

            @Override
            public synchronized void mark(int readlimit) {
                bais.mark(readlimit);
            }

            @Override
            public synchronized void reset() throws IOException {
                bais.reset();
            }

            @Override
            public void close() throws IOException {
                bais.close();
            }

            @Override
            public boolean isFinished() {
                return bais.available() <= 0;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }

    /**
     * The default behavior of this method is to return
     * getParameter(String name) on the wrapped request object.
     *
     * @param name
     */
    @Override
    public String getParameter(String name) {
        return super.getParameter(name);
    }

    /**
     * The default behavior of this method is to return getParameterMap()
     * on the wrapped request object.
     */
    @Override
    public Map<String, String[]> getParameterMap() {
        return super.getParameterMap();
    }

    /**
     * The default behavior of this method is to return getParameterNames()
     * on the wrapped request object.
     */
    @Override
    public Enumeration<String> getParameterNames() {
        return super.getParameterNames();
    }

    /**
     * The default behavior of this method is to return
     * getParameterValues(String name) on the wrapped request object.
     *
     * @param name
     */
    @Override
    public String[] getParameterValues(String name) {
        return super.getParameterValues(name);
    }
}